package com.learning.servlet2x.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * ClassName: CustomListener
 * Description: 实现单态登录的监听器：同一个账号只允许一个客户端登录
 * 1、实现 Session属性监听器 HttpSessionAttributeListener
 * Date: 2014-12-10 22:25 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
public class LoginSessionListener implements HttpSessionAttributeListener {


    /**
     * 保存 Session
     */
    Map<String, HttpSession> map = new HashMap<String, HttpSession>();

    /**
     * Session 属性添加
     *
     * @param event
     */
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {

        // 被添加进来的属性名称
        String name = event.getName();

        // 登录
        if (name.equals(PersonInfo.SESSION_PERSON)) {

            PersonInfo personInfo = (PersonInfo) event.getValue();

            // map 中有记录，表明该帐号在其他机器上登录过，将以前的登录失效
            HttpSession session = map.get(personInfo.getAccount());
            if (session != null) {
                //map 已经存在的旧的信息
                PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute(PersonInfo.SESSION_PERSON);
                System.out.println("帐号" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已经登录，该登录将被迫下线。");
                session.removeAttribute(PersonInfo.SESSION_PERSON);
                session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录，您被迫下线。");
            }

            // 将session以用户名为索引，放入map中
            map.put(personInfo.getAccount(), event.getSession());
            System.out.println("帐号" + personInfo.getAccount() + "在" + personInfo.getIp() + "登录。");
        }
    }

    /**
     * Session 属性删除
     *
     * @param event
     */
    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {

        String name = event.getName();

        // 注销
        if (name.equals(PersonInfo.SESSION_PERSON)) {
            // 将该 session 从 map 中移除
            PersonInfo personInfo = (PersonInfo) event.getValue();
            map.remove(personInfo.getAccount());
            System.out.println("帐号" + personInfo.getAccount() + "注销。");
        }
    }

    /**
     * Session 属性修改
     * 没有注销的情况下，用另一个帐号登录
     *
     * @param event
     */
    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {

        // 被修改的属性名
        String name = event.getName();

        // 没有注销的情况下，用另一个帐号登录
        if (name.equals(PersonInfo.SESSION_PERSON)) {

            // 移除旧的的登录信息
            PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
            map.remove(oldPersonInfo.getAccount());

            // 新的登录信息
            PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute(PersonInfo.SESSION_PERSON);

            // 也要检查新登录的帐号是否在别的机器上登录过
            if (map.get(personInfo.getAccount()) != null) {
                // map 中有记录，表明该帐号在其他机器上登录过，将以前的登录失效
                HttpSession session = map.get(personInfo.getAccount());
                session.removeAttribute(PersonInfo.SESSION_PERSON);
                session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录，您被迫下线。");
            }
            map.put(PersonInfo.SESSION_PERSON, event.getSession());
        }

    }


    class PersonInfo implements Serializable {

        private static final long serialVersionUID = 4063725584941336123L;

        public static final String SESSION_PERSON = "_PERSON_";
        public static final String SESSION_MSG = "_MSG_";

        /**
         * 帐号
         */
        private String account;

        /**
         * 登录IP地址
         */
        private String ip;

        /**
         * 登录时间
         */
        private Date loginDate;


        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            PersonInfo that = (PersonInfo) o;
            return Objects.equals(account, that.account) &&
                    Objects.equals(ip, that.ip) &&
                    Objects.equals(loginDate, that.loginDate);
        }

        @Override
        public int hashCode() {
            return Objects.hash(account, ip, loginDate);
        }

        public String getAccount() {
            return account;
        }

        public void setAccount(String account) {
            this.account = account;
        }

        public String getIp() {
            return ip;
        }

        public void setIp(String ip) {
            this.ip = ip;
        }

        public Date getLoginDate() {
            return loginDate;
        }

        public void setLoginDate(Date loginDate) {
            this.loginDate = loginDate;
        }
    }
}
